/**
 * Copyright (c) 2009-2011, chunquedong(YangJiandong)
 * 
 * This file is part of ChunMap project
 * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE(Version >=3)
 * 
 * History:
 *     2010-05-05  Jed Young  Creation
 */
package chunmap.model.geom;

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import chunmap.model.coord.Coordinate2D;
import chunmap.model.coord.Position;
import chunmap.model.coord.PrecisionModel;
import chunmap.model.elem.Envelope;
import chunmap.util.Logger;
import chunmap.util.LoggerTest;

/**
 * 读取WKT字符串表示的几何体
 * 
 * @author chunquedong
 * 
 */
public class WktReader {
	private static final Logger Log = new Logger(Logger.Info,LoggerTest.class.getName());

	private final PrecisionModel precision;

	public WktReader() {
		precision = PrecisionModel.getDoubleFloatModel();
	}

	public WktReader(PrecisionModel precision) {
		this.precision = precision;
	}

	public PrecisionModel getPrecision() {
		return precision;
	}

	public Geometry read(String text) {
		try {
			int tIndex = text.indexOf('(');
			String type = text.substring(0, tIndex);
			text = text.substring(tIndex);

			if (type.equalsIgnoreCase("POINT"))
				return new GeoPoint(getPoint(text));
			else if (type.equalsIgnoreCase("LINESTRING"))
				return getLineString(text);
			else if (type.equalsIgnoreCase("POLYGON"))
				return getPolygon(text);
			else if (type.equalsIgnoreCase("MULTIPOINT"))
				return getMultiPoint(text);
			else if (type.equalsIgnoreCase("MULTILINESTRING"))
				return getMultiPath(text);
			else if (type.equalsIgnoreCase("MULTIPOLYGON"))
				return getMultiPolygon(text);
			else if (type.equalsIgnoreCase("GEOMETRYCOLLECTION"))
				return getMultiGeometry(text);
			else
				throw new IllegalArgumentException("Unknow geometry");
		} catch (Exception ex) {
			WktParseException ex2 = new WktParseException();
			ex2.initCause(ex);
			throw ex2;
		}
	}

	public Envelope readEnvelop(String text) {
		try {
			int tIndex = text.indexOf('(');
			String type = text.substring(0, tIndex);
			text = text.substring(tIndex);

			if (type.equalsIgnoreCase("BOX")) {
				String[] str = text.split(",");

				Position p1 = getPoint(str[0]);
				Position p2 = getPoint(str[1]);

				return new Envelope(p1, p2);
			} else
				throw new IllegalArgumentException("Unknow geometry");

		} catch (Exception ex) {
			WktParseException ex2 = new WktParseException();
			ex2.initCause(ex);
			throw ex2;
		}
	}

	// ------------------------------------------------------------

	private Position getPoint(String text) {
		text = text.trim();
		text = removeBrackets(text);
		return readPoint(text);
	}

	private Position readPoint(String text) {
		text = text.trim();
		int xIndex = text.indexOf(' ');

		double x = Double.parseDouble(text.substring(0, xIndex));
		double y = Double.parseDouble(text.substring(xIndex));

		Position p = new Coordinate2D(x, y);
		return p;
	}

	private LineString getLineString(String text) {
		text = text.trim();
		List<Position> points = readPointList(text);
		return new LineString(points);
	}

	private List<Position> readPointList(String text) {
		text = text.trim();
		List<Position> points = new ArrayList<Position>();
		String[] str = text.split(",");

		for (String s : str) {
			Position p = getPoint(s);
			points.add(p);
		}
		return points;
	}

	private MultiGeometry<Geometry> getMultiGeometry(String text) {
		text = text.trim();
		text = text.substring(1, text.length() - 1);
		
		List<Geometry> geos = new ArrayList<Geometry>();

		String regEx = ",[A-Z]";
		Pattern pat = Pattern.compile(regEx);
		Matcher mat = pat.matcher(text);

		int i = 0;
		int j = 0;
		while (mat.find()) {
			j = mat.start();
			String s = text.substring(i, j);
			i = j + 1;

			Log.log(Logger.Debug, s);
			Geometry g = read(s);
			geos.add(g);
		}

		Log.log(Logger.Debug, text.substring(i, text.length()));
		Geometry g = read(text.substring(i, text.length()));
		geos.add(g);

		return new MultiGeometry<Geometry>(geos);
	}

	private Polygon getPolygon(String text) {
		text = text.trim();
		String[] str = text.split("\\),\\(");
		List<Ring> rings = new ArrayList<Ring>();

		for (String s : str) {
			Ring r = getLineString(s).toLinearRing();
			rings.add(r);
		}
		return new Polygon(rings.get(0), rings.subList(1, rings.size()));
	}

	private MultiPoint getMultiPoint(String text) {
		text = text.trim();
		List<Position> points = readPointList(text);
		
		List<GeoPoint> geoPointList=new ArrayList<GeoPoint>();
		for(Position p:points){
			geoPointList.add(new GeoPoint(p));
		}
		
		return new MultiPoint(geoPointList);
	}

	private MultiLineString getMultiPath(String text) {
		text = text.trim();
		String[] str = text.split("\\),\\(");
		List<LineString> lines = new ArrayList<LineString>();
		for (String s : str) {
			LineString ls = getLineString(s);
			lines.add(ls);
		}
		return new MultiLineString(lines);
	}

	private MultiPolygon getMultiPolygon(String text) {
		text = text.trim();
		String[] str = text.split("\\)\\),\\(\\(");
		List<Polygon> polygons = new ArrayList<Polygon>();
		for (String s : str) {
			Polygon pg = getPolygon(s);
			polygons.add(pg);
		}
		return new MultiPolygon(polygons);
	}

	private String removeBrackets(String text) {
		text = text.replace('(', ' ');
		text = text.replace(')', ' ');
		return text.trim();
	}
}